<template>
  <div class="code-editor-container">
    <!-- 头部工具栏 -->
    <div class="toolbar">
      <el-select v-model="currentLanguage" @change="handleLanguageChange" style="width: 120px">
        <!-- <el-option label="HTML" value="html" /> -->
        <el-option label="Vue" value="vue" />
      </el-select>
      <!-- <el-button type="primary" @click="runCode">
        <el-icon><VideoPlay /></el-icon>
        运行代码
      </el-button> -->
      <el-button type="success" @click="saveAndRun">
        <el-icon><DocumentAdd /></el-icon>
        保存
      </el-button>
      <el-button @click="clearCode">
        <el-icon><Delete /></el-icon>
        清空
      </el-button>
      <el-button @click="$router.back()">
        <el-icon><ArrowLeft /></el-icon>
        返回
      </el-button>
    </div>

    <!-- 编辑器和预览区域 -->
    <div class="editor-layout">
      <!-- 代码编辑器 -->
      <div class="editor-panel">
        <div class="panel-header">
          <span>代码编辑器</span>
          <span class="language-tag">{{ currentLanguage.toUpperCase() }}</span>
        </div>
        <div ref="editorRef" class="editor-content"></div>
      </div>

      <!-- 预览区域 -->
      <div class="preview-panel">
        <div class="panel-header">
          <span>预览</span>
          <el-button size="small" @click="refreshPreview">
            <el-icon><Refresh /></el-icon>
            刷新
          </el-button>
        </div>
        <div class="preview-content">
          <div v-if="isLoading" class="loading-overlay">
            <div class="loading-text">加载中...</div>
          </div>
          <div v-if="errorMessage" class="error-overlay">
            <div class="error-text">{{ errorMessage }}</div>
          </div>
          <iframe ref="previewRef" class="preview-iframe" sandbox="allow-scripts allow-same-origin"></iframe>
        </div>
      </div>
    </div>

    <!-- 保存对话框 -->
    <el-dialog v-model="saveDialogVisible" title="保存组件" width="500px" :before-close="cancelSave">
      <el-form :model="saveForm" label-width="120px">
        <el-form-item label="组件名称" required>
          <el-input v-model="saveForm.cubeName" placeholder="请输入组件名称" clearable />
        </el-form-item>

        <el-form-item label="文件名称" required>
          <el-input v-model="saveForm.bindFileName" placeholder="请输入文件名称（英文大写开头）" @input="handleFileNameChange" clearable :disabled="isEdit">
            <template #append>.vue</template>
          </el-input>
          <div class="form-tip">文件名必须以英文大写字母开头，如：Tea、Button等</div>
        </el-form-item>

        <el-form-item label="组件标签">
          <el-input v-model="saveForm.bindComponentString" placeholder="自动生成" readonly disabled />
          <div class="form-tip">根据文件名自动生成组件标签</div>
        </el-form-item>

        <el-form-item label="组件类型">
          <el-select v-model="saveForm.type" style="width: 100%" :disabled="isEdit">
            <el-option label="普通组件" value="normal" />
            <el-option label="轮播组件" value="carousel" />
          </el-select>
        </el-form-item>
      </el-form>

      <template #footer>
        <span class="dialog-footer">
          <el-button @click="cancelSave">取消</el-button>
          <el-button type="primary" @click="confirmSave">确认保存</el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, nextTick, onUnmounted } from "vue";
import { ElMessage } from "element-plus";
import { VideoPlay, Delete, MagicStick, Refresh, DocumentAdd, ArrowLeft } from "@element-plus/icons-vue";
import { EditorView, basicSetup } from "codemirror";
import { EditorState } from "@codemirror/state";
import { html } from "@codemirror/lang-html";
import { vue } from "@codemirror/lang-vue";
import { oneDarkTheme } from "@codemirror/theme-one-dark";
import { getCubeById } from "@/api/webbase";
import { createCube, updateCube } from "@/api/webbase";
import { useRoute } from "vue-router";

// 响应式数据
const editorRef = ref<HTMLElement>();
const previewRef = ref<HTMLIFrameElement>();
const editorView = ref<EditorView>();
const currentLanguage = ref("vue");
const templateContent = ref("");
const isLoading = ref(false);
const errorMessage = ref("");
const route = useRoute();
const isEdit = route.query.id !== undefined;

// 消息监听器的引用，用于清理
let messageListener: ((event: MessageEvent) => void) | null = null;
let timeoutId: number | null = null;

// 默认代码模板
const defaultTemplates = {
  html: `<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>HTML 示例</title>
    <style>
        .container {
            max-width: 800px;
            margin: 0 auto;
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        h1 {
            color: #333;
            text-align: center;
        }
        .btn {
            background: #007bff;
            color: white;
            padding: 10px 20px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        .btn:hover {
            background: #0056b3;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>欢迎使用代码编辑器</h1>
        <p>这是一个基于 CodeMirror 6 的在线代码编辑器示例。</p>
        <button class="btn" onclick="showAlert()">点击我</button>
    </div>

    <script>
        function showAlert() {
            alert('Hello, CodeMirror 6!');
        }
    <\/script>
<\/body>
<\/html>`,
  vue: `<template>
 <div class="component-wrapper">
   你好，开始编辑你的模块
 </div>
</template>
<script setup>
<\/script>
<style scoped>
  .component-wrapper {
    font-size: 16px;
    font-weight: bold;
  }
<\/style>`
};

/**
 * 提取某个 block（template/script）
 * @param code 源代码
 * @param tag 标签名
 * @returns 提取的内容
 */
function extract(code: string, tag: string): string {
  // 处理 script setup 的特殊情况
  if (tag === "script setup") {
    const reg = /<script\s+setup[^>]*?>([\s\S]*?)<\/script>/;
    const match = code.match(reg);
    return match?.[1]?.trim() || "";
  }

  const reg = new RegExp(`<${tag}[^>]*?>([\\s\\S]*?)<\\/${tag}>`);
  const match = code.match(reg);
  return match?.[1]?.trim() || "";
}

/**
 * 提取需要在模板中使用的变量名
 * @param script 脚本内容
 * @returns 变量名字符串
 */
function extractRefs(script: string): string {
  const allVars = new Set<string>();

  // 1. Vue 响应式变量
  const refMatches = [...script.matchAll(/const\s+(\w+)\s*=\s*ref\s*\(/g)];
  refMatches.forEach((m) => allVars.add(m[1]));

  const reactiveMatches = [...script.matchAll(/const\s+(\w+)\s*=\s*reactive\s*\(/g)];
  reactiveMatches.forEach((m) => allVars.add(m[1]));

  const computedMatches = [...script.matchAll(/const\s+(\w+)\s*=\s*computed\s*\(/g)];
  computedMatches.forEach((m) => allVars.add(m[1]));

  // 2. 数据结构
  const arrayMatches = [...script.matchAll(/const\s+(\w+)\s*=\s*\[/g)];
  arrayMatches.forEach((m) => allVars.add(m[1]));

  const objectMatches = [...script.matchAll(/const\s+(\w+)\s*=\s*\{/g)];
  objectMatches.forEach((m) => allVars.add(m[1]));

  // 3. 函数定义
  const arrowFuncMatches = [...script.matchAll(/const\s+(\w+)\s*=\s*(?:async\s+)?\([^)]*\)\s*=>/g)];
  arrowFuncMatches.forEach((m) => allVars.add(m[1]));

  const funcExprMatches = [...script.matchAll(/const\s+(\w+)\s*=\s*(?:async\s+)?function\s*\(/g)];
  funcExprMatches.forEach((m) => allVars.add(m[1]));

  const funcDeclMatches = [...script.matchAll(/(?:async\s+)?function\s+(\w+)\s*\(/g)];
  funcDeclMatches.forEach((m) => allVars.add(m[1]));

  // 4. 基本类型变量
  const stringMatches = [...script.matchAll(/const\s+(\w+)\s*=\s*['"`]/g)];
  stringMatches.forEach((m) => allVars.add(m[1]));

  const numberMatches = [...script.matchAll(/const\s+(\w+)\s*=\s*-?\d+(?:\.\d+)?/g)];
  numberMatches.forEach((m) => allVars.add(m[1]));

  const boolMatches = [...script.matchAll(/const\s+(\w+)\s*=\s*(?:true|false)/g)];
  boolMatches.forEach((m) => allVars.add(m[1]));

  // 5. let 和 var 声明
  const letMatches = [...script.matchAll(/let\s+(\w+)/g)];
  letMatches.forEach((m) => allVars.add(m[1]));

  const varMatches = [...script.matchAll(/var\s+(\w+)/g)];
  varMatches.forEach((m) => allVars.add(m[1]));

  // 6. 解构赋值
  const destructureMatches = [...script.matchAll(/const\s*\{\s*([^}]+)\s*\}/g)];
  destructureMatches.forEach((m) => {
    const vars = m[1].split(",").map((v) => v.trim().split(":")[0].trim());
    vars.forEach((v) => allVars.add(v));
  });

  const arrayDestructureMatches = [...script.matchAll(/const\s*\[\s*([^\]]+)\s*\]/g)];
  arrayDestructureMatches.forEach((m) => {
    const vars = m[1].split(",").map((v) => v.trim());
    vars.forEach((v) => {
      if (v && !v.includes("...")) allVars.add(v);
    });
  });

  return Array.from(allVars).join(", ");
}

/**
 * 生成Vue3预览HTML
 * @param sourceCode Vue单文件组件源代码
 * @returns 预览HTML
 */
function generateHTML(sourceCode: string): string {
  const template = extract(sourceCode, "template");
  const script = extract(sourceCode, "script setup");
  const style = extract(sourceCode, "style");
  const refVars = extractRefs(script);

  // 移除 import 语句，因为我们使用全局变量
  const cleanScript = script.replace(/import\s+.*?from\s+['"].*?['"];?\s*/g, "");

  // 替换 script 结束标签，防止提前结束
  const safeScript = cleanScript.replace(/<\/script>/gi, "<\\/script>");

  // 处理样式，移除scoped属性相关内容
  const cleanStyle = style.replace(/scoped/g, "");

  return `<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Preview</title>
  <link rel="stylesheet" href="https://unpkg.com/element-plus@2.10.5/dist/index.css" />
  <style>
    body { margin: 0; padding: 10px; font-family: sans-serif; }
    ${cleanStyle}
  </style>
  <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"><\/script>
  <script src="https://unpkg.com/element-plus@2.10.5/dist/index.full.min.js"><\/script>
</head>
<body>
  <div id="app"></div>
  <script type="module">
    // 通知父窗口开始加载
    if (window.parent !== window) {
      window.parent.postMessage({ type: 'preview-loading' }, '*');
    }
    // 等待DOM完全加载
    document.addEventListener('DOMContentLoaded', function() {
      try {
        // 检查必要的全局变量是否已加载
        if (!window.Vue) {
          const appElement = document.getElementById('app');
          if (appElement) {
            appElement.innerHTML = '<div style="padding: 20px; color: red;">错误: Vue 未能正确加载</div>';
          }
          if (window.parent !== window) {
            window.parent.postMessage({ type: 'preview-error', message: 'Vue 未能正确加载' }, '*');
          }
          return;
        }
        if (!window.ElementPlus) {
          const appElement = document.getElementById('app');
          if (appElement) {
            appElement.innerHTML = '<div style="padding: 20px; color: red;">错误: Element Plus 未能正确加载</div>';
          }
          if (window.parent !== window) {
            window.parent.postMessage({ type: 'preview-error', message: 'Element Plus 未能正确加载' }, '*');
          }
          return;
        }
        const { createApp, ref } = Vue;
        const App = {
          template: \`${template}\`,
          setup() {
            try {
              ${safeScript}
              return { ${refVars} }
            } catch (error) {
              console.error('脚本执行错误:', error);
              if (window.parent !== window) {
                window.parent.postMessage({ type: 'preview-error', message: \`脚本执行错误: \${error.message}\` }, '*');
              }
              return { error: error.message };
            }
          }
        };
        const app = createApp(App);
        app.use(ElementPlus);

        // 添加全局错误处理
        app.config.errorHandler = (err, vm, info) => {
          console.error('Vue应用错误:', err, info);
          const appElement = document.getElementById('app');
          if (appElement) {
            appElement.innerHTML = \`<div style="padding: 20px; color: red;">运行时错误: \${err.message}</div>\`;
          }
          if (window.parent !== window) {
            window.parent.postMessage({ type: 'preview-error', message: \`运行时错误: \${err.message}\` }, '*');
          }
        };

        app.mount('#app');

        // 通知父窗口加载成功
        if (window.parent !== window) {
          window.parent.postMessage({ type: 'preview-ready' }, '*');
        }

      } catch (error) {
        console.error('初始化错误:', error);
        const appElement = document.getElementById('app');
        if (appElement) {
          appElement.innerHTML = \`<div style="padding: 20px; color: red;">初始化错误: \${error.message}\`;
        }
        if (window.parent !== window) {
          window.parent.postMessage({ type: 'preview-error', message: \`初始化错误: \${error.message}\` }, '*');
        }
      }
    });
  <\/script>
</body>
</html>`;
}

/**
 * 获取语言扩展
 * @param language 编程语言
 * @returns CodeMirror 语言扩展
 */
const getLanguageExtension = (language: string) => {
  switch (language) {
    case "html":
      return html();
    case "vue":
      return vue();
    default:
      return vue();
  }
};

/**
 * 初始化编辑器
 */
const initEditor = () => {
  if (editorView.value) {
    editorView.value.destroy();
  }

  if (!editorRef.value) return;

  const startState = EditorState.create({
    doc: templateContent.value || defaultTemplates[currentLanguage.value as keyof typeof defaultTemplates],
    extensions: [
      basicSetup,
      oneDarkTheme,
      getLanguageExtension(currentLanguage.value),
      EditorView.updateListener.of((update) => {
        if (update.docChanged) {
          templateContent.value = update.state.doc.toString();
        }
      })
    ]
  });

  editorView.value = new EditorView({
    state: startState,
    parent: editorRef.value
  });
};

/**
 * 处理语言切换
 * @param language 新选择的语言
 */
const handleLanguageChange = (language: string) => {
  templateContent.value = defaultTemplates[language as keyof typeof defaultTemplates];
  nextTick(() => {
    initEditor();
  });
};

/**
 * 运行代码
 */
const runCode = () => {
  const iframe = previewRef.value;
  if (!iframe) {
    errorMessage.value = "iframe元素未找到";
    return;
  }

  try {
    // 清理之前的监听器和超时
    if (messageListener) {
      window.removeEventListener("message", messageListener);
      messageListener = null;
    }
    if (timeoutId) {
      clearTimeout(timeoutId);
      timeoutId = null;
    }

    isLoading.value = true;
    errorMessage.value = "";

    const doc = iframe.contentDocument || iframe.contentWindow?.document;
    if (!doc) {
      errorMessage.value = "iframe文档无法访问";
      isLoading.value = false;
      return;
    }

    const code = templateContent.value;
    if (currentLanguage.value === "html") {
      // HTML 代码直接在 iframe 中显示
      doc.open();
      doc.write(code);
      doc.close();
      isLoading.value = false;
      ElMessage.success("代码运行成功");
    } else if (currentLanguage.value === "vue") {
      // Vue 代码需要特殊处理
      // 设置消息监听器
      messageListener = (event: MessageEvent) => {
        // 确保消息来自我们的iframe
        if (event.source !== iframe.contentWindow) return;

        const { type, message } = event.data;

        switch (type) {
          case "preview-loading":
            // iframe开始加载，保持loading状态
            break;
          case "preview-ready":
            // iframe加载成功
            isLoading.value = false;
            errorMessage.value = "";
            if (timeoutId) {
              clearTimeout(timeoutId);
              timeoutId = null;
            }
            ElMessage.success("代码运行成功");
            break;
          case "preview-error":
            // iframe内部发生错误
            isLoading.value = false;
            errorMessage.value = message || "预览加载失败";
            if (timeoutId) {
              clearTimeout(timeoutId);
              timeoutId = null;
            }
            break;
        }
      };

      window.addEventListener("message", messageListener);

      // 写入HTML内容
      doc.open();
      doc.write(generateHTML(code));
      doc.close();

      // 设置超时处理（增加到10秒）
      timeoutId = window.setTimeout(() => {
        if (isLoading.value) {
          isLoading.value = false;
          errorMessage.value = "加载超时，可能是网络连接问题或代码存在错误";
        }
        timeoutId = null;
      }, 10000);
    }
  } catch (error) {
    isLoading.value = false;
    errorMessage.value = `更新失败: ${error instanceof Error ? error.message : "未知错误"}`;
    console.error("iframe更新错误:", error);
  }
};

/**
 * 清空代码
 */
const clearCode = () => {
  if (editorView.value) {
    editorView.value.dispatch({
      changes: {
        from: 0,
        to: editorView.value.state.doc.length,
        insert: ""
      }
    });
    templateContent.value = "";
  }
  ElMessage.success("代码已清空");
};

/**
 * 格式化代码
 */
const formatCode = () => {
  // 简单的格式化逻辑，实际项目中可以使用 prettier 等工具
  ElMessage.info("格式化功能待完善");
};

/**
 * 刷新预览
 */
const refreshPreview = () => {
  runCode();
};

// 保存对话框相关数据
const saveDialogVisible = ref(false);
const saveForm = ref({
  cubeName: "",
  bindFileName: "",
  bindComponentString: "",
  type: "normal"
});

/**
 * 保存并运行代码
 */
const saveAndRun = () => {
  saveDialogVisible.value = true;
};

/**
 * 处理文件名变化，自动生成组件字符串
 * @param fileName 文件名
 */
const handleFileNameChange = (fileName: string) => {
  if (fileName) {
    // 确保首字母大写
    const formattedName = fileName.charAt(0).toUpperCase() + fileName.slice(1);
    saveForm.value.bindFileName = formattedName;
    saveForm.value.bindComponentString = `<${formattedName} />`;
  }
};

/**
 * 确认保存
 */
const confirmSave = async () => {
  try {
    // 验证必填字段
    if (!saveForm.value.cubeName.trim()) {
      ElMessage.error("请输入组件名称");
      return;
    }
    if (!saveForm.value.bindFileName.trim()) {
      ElMessage.error("请输入文件名称");
      return;
    }

    // 验证文件名格式（英文大写开头）
    const fileNamePattern = /^[A-Z][a-zA-Z0-9]*$/;
    if (!fileNamePattern.test(saveForm.value.bindFileName)) {
      ElMessage.error("文件名称必须以英文大写字母开头，且只能包含字母和数字");
      return;
    }

    const saveData = {
      cubeName: saveForm.value.cubeName.trim(),
      bindFileName: saveForm.value.bindFileName.trim(),
      bindComponentString: saveForm.value.bindComponentString.trim(),
      bindFileCache: templateContent.value,
      type: saveForm.value.type
    };

    // 检查是否为编辑模式
    const editId = route.query.id as string;

    if (editId) {
      // 编辑模式：调用更新接口
      await updateCube(editId, saveData);
      ElMessage.success("组件更新成功");
    } else {
      // 新增模式：调用创建接口
      await createCube(saveData);
      ElMessage.success("组件保存成功");
    }
    saveDialogVisible.value = false;

    // 重置表单
    saveForm.value = {
      cubeName: "",
      bindFileName: "",
      bindComponentString: "",
      type: "normal"
    };
  } catch (error) {
    console.error("保存失败:", error);
    ElMessage.error("保存失败，请重试");
  }
};

/**
 * 取消保存
 */
const cancelSave = () => {
  saveDialogVisible.value = false;
  // 重置表单
  saveForm.value = {
    cubeName: "",
    bindFileName: "",
    bindComponentString: "",
    type: "normal"
  };
};

/**
 * 处理键盘事件
 * @param event 键盘事件
 */
const handleKeyDown = (event: KeyboardEvent) => {
  // 检测 Ctrl+S 组合键
  if (event.ctrlKey && event.key === "s") {
    event.preventDefault(); // 阻止浏览器默认保存行为
    saveAndRun();
  }
};

/**
 * 初始化预览区域
 */
const initPreview = () => {
  if (!previewRef.value) return;

  const doc = previewRef.value.contentDocument;
  if (doc) {
    doc.open();
    doc.write(
      '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>预览</title></head><body><div style="padding: 20px; text-align: center; color: #666;">请点击"运行代码"按钮查看效果</div></body></html>'
    );
    doc.close();
  }
};

/**
 * 加载编辑数据
 * @param id 组件ID
 */
const loadEditData = async (id: string) => {
  try {
    const response = await getCubeById(id);
    // @ts-ignore
    if (response.success && response.data) {
      // @ts-ignore
      const { cubeName, bindFileName, bindComponentString, bindFileCache, type } = response.data;

      // 填充保存表单数据
      saveForm.value = {
        cubeName: cubeName || "",
        bindFileName: bindFileName || "",
        bindComponentString: bindComponentString || "",
        type: type || "normal"
      };

      // 设置编辑器内容
      if (bindFileCache) {
        templateContent.value = bindFileCache;
        // 如果编辑器已初始化，更新内容
        if (editorView.value) {
          editorView.value.dispatch({
            changes: {
              from: 0,
              to: editorView.value.state.doc.length,
              insert: bindFileCache
            }
          });
          runCode();
        }
      }

      ElMessage.success("编辑数据加载成功");
    } else {
      ElMessage.error("获取编辑数据失败");
    }
  } catch (error) {
    console.error("加载编辑数据失败:", error);
    ElMessage.error("加载编辑数据失败，请重试");
  }
};

// 组件挂载时初始化编辑器和预览
onMounted(() => {
  // 检查是否为编辑模式
  const editId = route.query.id as string;

  // 设置默认模板内容
  templateContent.value = defaultTemplates[currentLanguage.value as keyof typeof defaultTemplates];

  nextTick(async () => {
    initEditor();
    initPreview();

    // 如果存在ID，则加载编辑数据
    if (editId) {
      await loadEditData(editId);
    }
  });

  // 添加键盘事件监听
  document.addEventListener("keydown", handleKeyDown);
});

// 组件卸载时清理资源
onUnmounted(() => {
  if (messageListener) {
    window.removeEventListener("message", messageListener);
    messageListener = null;
  }
  if (timeoutId) {
    clearTimeout(timeoutId);
    timeoutId = null;
  }

  // 移除键盘事件监听
  document.removeEventListener("keydown", handleKeyDown);
});
</script>

<style lang="less" scoped>
.code-editor-container {
  height: calc(100vh - 140px);
  display: flex;
  flex-direction: column;
  background: #f5f5f5;
}

.toolbar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px;
  background: white;
  border-bottom: 1px solid #e0e0e0;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.editor-layout {
  flex: 1;
  display: flex;
  gap: 1px;
  background: #e0e0e0;
  height: 100%;
}

.editor-panel,
.preview-panel {
  flex: 1;
  display: flex;
  flex-direction: column;
  background: white;
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 16px;
  background: #fafafa;
  border-bottom: 1px solid #e0e0e0;
  font-weight: 500;
  color: #333;
}

.language-tag {
  background: #007bff;
  color: white;
  padding: 4px 8px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: normal;
}

.editor-content {
  flex: 1;
  overflow: hidden;

  :deep(.cm-editor) {
    height: 100%;
    font-size: 14px;
  }

  :deep(.cm-scroller) {
    font-family: "Fira Code", "Monaco", "Menlo", "Ubuntu Mono", monospace;
  }
}

.preview-content {
  flex: 1;
  padding: 0;
  background: white;
  position: relative;
}

.preview-iframe {
  width: 100%;
  height: 100%;
  border: none;
  background: white;
}

.loading-overlay,
.error-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10;
  border-radius: 6px;
}

.loading-overlay {
  background-color: rgba(255, 255, 255, 0.9);
}

.error-overlay {
  background-color: rgba(254, 226, 226, 0.95);
}

.loading-text {
  color: #409eff;
  font-size: 16px;
  font-weight: 500;
}

.error-text {
  color: #f56c6c;
  font-size: 14px;
  text-align: center;
  padding: 20px;
  max-width: 80%;
  word-wrap: break-word;
}

/* 表单提示样式 */
.form-tip {
  font-size: 12px;
  color: #909399;
  margin-top: 4px;
  line-height: 1.4;
}

/* 对话框样式 */
.dialog-footer {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
}

/* 响应式设计 */
@media (max-width: 768px) {
  .editor-layout {
    flex-direction: column;
  }

  .editor-panel,
  .preview-panel {
    min-height: 300px;
  }

  .toolbar {
    flex-wrap: wrap;
    gap: 8px;
  }
}
</style>
